import copy
import random

from Layer import utils

RED, BLUE, GREEN = 0, 1, -1  # 拷贝自run_offline_games.py


class InfoWarEnv:
    def __init__(self):
        utils.set_current_actions([])  # 初始时创建空的动作列表

    def setup(self):
        pass

    def step(self, actions):
        '''
        维护当前动作列表，在维护的同时还会对已完成动作进行裁决
        返回值：logs用于自建web界面的复盘展示；msg_action用于在庙算平台中输出信息战的全局播报消息
        '''
        # 将上一轮是否有通信中断的算子做初始化
        utils.set_last_step_communication_stop({
            "red": False,
            "blue": False,
        })
        # 创建本轮日志对象
        logs = {
            "exec_actions": [],
            "ops_status": {},
            "network_status": {}
        }
        sendMsg_actions = []
        # 1、将新动作添加到动作列表中
        current_actions = utils.get_current_actions()
        current_actions += actions
        # 2、对动作列表中的内容更新。具体来说，先将列表中所有动作时间-1，如果剩余时间为0则从列表中剔除，并进行裁决
        for action in current_actions:
            action["remain_time"] -= 1
        # 由于想要在遍历列表时删除其中元素，所以此处拷贝动作列表，遍历新列表但删除的是原列表中的元素
        temp_list = copy.deepcopy(current_actions)
        for action in temp_list[:]:
            if action["remain_time"] == 0:
                self.judge(action, logs, sendMsg_actions)  # 对该动作进行裁决
                current_actions.remove(action)
        # 3、检查原有动作是否仍具有可执行性，已经丧失执行成功可能性的动作就从动作列表中剔除，并添加到打印日志中
        self.eliminate_invalid_actions(current_actions, logs, sendMsg_actions)
        utils.set_current_actions(current_actions)  # 将修改好的动作列表写回去
        # 4、填充日志并返回。注意：此处需要采用深拷贝来保存每个step的一个数据副本
        logs["ops_status"]["red"] = copy.deepcopy(utils.get_ops_status(RED))
        logs["ops_status"]["blue"] = copy.deepcopy(utils.get_ops_status(BLUE))
        logs["network_status"]["red"] = copy.deepcopy(utils.get_network_status(RED))
        logs["network_status"]["blue"] = copy.deepcopy(utils.get_network_status(BLUE))
        # 5、恢复一定的红蓝双方的算力资源，并进行播报
        utils.set_current_computing_resources(RED, utils.get_current_computing_resources(RED) + 8)
        utils.set_current_computing_resources(BLUE, utils.get_current_computing_resources(BLUE) + 8)
        # msg_body = "【红方】我方还剩下的算力资源量为：" + str(utils.get_current_computing_resources(RED))
        # sendMsg_action = utils.send_Msg_2_All(RED, msg_body)
        # if sendMsg_action is not None:
        #     sendMsg_actions.append(sendMsg_action)
        # msg_body = "【蓝方】我方还剩下的算力资源量为：" + str(utils.get_current_computing_resources(BLUE))
        # sendMsg_action = utils.send_Msg_2_All(BLUE, msg_body)
        # if sendMsg_action is not None:
        #     sendMsg_actions.append(sendMsg_action)
        # 6、向日志中统计一些全局信息
        # 统计通信质量百分比
        logs["communication_quality"] = utils.statistics_communication_quality()
        # 记录当前step的伪装接入成功率
        disguised_access_success_rete = {
            "red": round(utils.ACTION_SUCC_RATE[401][0], 2),
            "blue": round(utils.ACTION_SUCC_RATE[401][1], 2),
        }
        logs["disguised_access_success_rete"] = disguised_access_success_rete
        # 记录红蓝双方本轮发起攻击的次数，并重置统计
        logs["current_attack_num"] = utils.get_current_attack_num()
        utils.set_current_attack_num({
            "red": 0,
            "blue": 0,
        })
        # 记录红蓝双方本轮攻击成功的次数，并重置统计
        logs["current_attack_success_num"] = utils.get_current_attack_success_num()
        utils.set_current_attack_success_num({
            "red": 0,
            "blue": 0,
        })
        return [logs, sendMsg_actions]

    def judge(self, action, logs, sendMsg_actions):
        '''
        裁决动作：
        1、记录动作日志
        2、根据概率表判定该动作是否成功，如果成功则根据动作效果修改全局变量
        '''
        # 1、将动作进行日志记录
        if action["type"] == 401:  # 伪装接入动作的成功率是动态可变的，动作是否成功需要单独进行判断
            is_success = random.random() <= utils.ACTION_SUCC_RATE[action["type"]][action["color"]]
        else:
            is_success = random.random() <= utils.ACTION_SUCC_RATE[action["type"]]
        if is_success:# 将成功的次数统计下来
            current_attack_success_num = utils.get_current_attack_success_num()
            current_attack_success_num["red" if action["color"] == 0 else "blue"] += 1
            utils.set_current_attack_num(current_attack_success_num)
        exec_action = {
            "sponsor": "red" if action["color"] == 0 else "blue",
            "type": action["type"],
            "ops_id": -1 if "ops_id" not in action else action["ops_id"],  # 没有目标算子属性的action，日志中该项则置为-1
            "result": "执行成功" if is_success else "执行失败"
        }
        logs["exec_actions"].append(exec_action)
        # 2、将动作执行结果通过庙算提供的发送消息动作展示到将庙算复盘界面中
        msg_body = ""
        msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
        if "ops_id" in action:
            msg_body += "对目标算子 " + str(action["ops_id"]) + " "

        msg_body += "发起动作--" + utils.get_info_action_name(action["type"]) + "--，"
        msg_body += "最终裁决结果为：" + "执行成功" if is_success else "执行失败"
        sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
        if sendMsg_action is not None:
            sendMsg_actions.append(sendMsg_action)
        # 3、根据概率表判定该动作是否成功，如果成功则根据动作效果修改全局变量
        if is_success:  # 如果动作成功，则产生动作效果
            type = action["type"]
            ops_id = -1
            if "ops_id" in action:
                ops_id = action["ops_id"]
            color = action["color"]
            network_status = utils.get_network_status(color)
            ops_status = utils.get_ops_status(color)
            peer_network_status = utils.get_network_status(1 - color)
            peer_ops_status = utils.get_ops_status(1 - color)
            if type == 101:
                pass
            elif type == 102:
                pass
            elif type == 103:
                network_status[ops_id] = 0.0
                msg_body = ""
                msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
                msg_body += "我方算子" + str(action["ops_id"]) + "与基地恢复至最佳通信质量"
                sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
                if sendMsg_action is not None:
                    sendMsg_actions.append(sendMsg_action)
            elif type == 104:
                # 先降低概率，再打印日志
                extent = utils.reduce_packet_loss(network_status)
                # msg_body = ""
                # msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
                # msg_body += "丢包率 ↑ ，幅度为：" + str(extent)
                # sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
                # if sendMsg_action is not None:
                #     sendMsg_actions.append(sendMsg_action)
            elif type == 201:
                for op_id, op_status in ops_status.items():
                    op_status["has_disguised_access"] = False
            elif type == 202:
                for op_id, op_status in ops_status.items():
                    op_status["has_implanted_virus"] = False
            elif type == 203:
                for op_id, op_status in ops_status.items():
                    op_status["has_stealed_infos"] = False
            elif type == 204:
                ops_status[ops_id]["has_cracked_key"] = False
            elif type == 205:
                utils.ACTION_SUCC_RATE[401][1 - action["color"]] = max(0, utils.ACTION_SUCC_RATE[401][
                    1 - action["color"]] - 0.1)
            elif type == 301 or type == 302:
                peer_ops_status[ops_id]["has_cracked_key"] = True
            elif type == 303:
                peer_network_status[ops_id] = 1.0
                msg_body = ""
                msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
                msg_body += "敌方算子" + str(action["ops_id"]) + "与基地断联，丧失行动能力"
                sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
                if sendMsg_action is not None:
                    sendMsg_actions.append(sendMsg_action)
                # 将本轮存在算子通信中断的情况记录下来
                last_step_communication_stop = utils.get_last_step_communication_stop()
                last_step_communication_stop["blue" if action["color"] == 0 else "red"] = True
                utils.set_last_step_communication_stop(last_step_communication_stop)
            elif type == 304:
                # 先增大概率，再打印日志，顺便将本轮存在算子通信中断的情况记录下来
                utils.increase_packet_loss(peer_network_status)
                for key in peer_network_status:
                    if abs(peer_network_status[key] - 1) < 0.0001:# 浮点数不能比等，而我们调整的最小幅度为0.01，所以此处使用0.0001来判断是否为1
                        last_step_communication_stop = utils.get_last_step_communication_stop()
                        last_step_communication_stop["blue" if action["color"] == 0 else "red"] = True
                        utils.set_last_step_communication_stop(last_step_communication_stop)
                        break
                # msg_body = ""
                # msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
                # msg_body += "丢包率 ↓ ，幅度为：" + str(extent)
                # sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
                # if sendMsg_action is not None:
                #     sendMsg_actions.append(sendMsg_action)
            elif type == 401:
                # 随机对一个算子伪装接入（ps：此时游戏还未结束，就说明）
                # 获取已成功伪装接入且未死亡的对方算子的id构成的列表
                peer_alive_ids = []
                for op_id, op_status in peer_ops_status.items():
                    if op_status["live_status"] == True:
                        peer_alive_ids.append(op_id)
                # 随机取出一个活着的算子id作为攻击对象，如果算子都死亡了则直接返回
                if len(peer_alive_ids) == 0:
                    return None
                ops_id = random.choice(peer_alive_ids)
                peer_ops_status[ops_id]["has_disguised_access"] = True
            elif type == 402:
                peer_ops_status[ops_id]["has_implanted_virus"] = True
            elif type == 403:
                peer_ops_status[ops_id]["has_stealed_infos"] = True
            elif type == 404:
                utils.ACTION_SUCC_RATE[401][action["color"]] = min(1,
                                                                   utils.ACTION_SUCC_RATE[401][action["color"]] + 0.1)
            elif type == 501:
                pass
            elif type == 502:
                pass
            utils.set_network_status(color, network_status)
            utils.set_ops_status(color, ops_status)
            utils.set_network_status(1 - color, peer_network_status)
            utils.set_ops_status(1 - color, peer_ops_status)

    def eliminate_invalid_actions(self, current_actions, logs, sendMsg_actions):
        '''
        检查原有动作是否仍具有可执行性，已经丧失执行成功可能性的动作就从动作列表中剔除，并添加到打印日志中
        '''
        # 由于想要在遍历列表时删除其中元素，所以此处拷贝动作列表，遍历新列表但删除的是原列表中的元素
        temp_list = copy.deepcopy(current_actions)
        for action in temp_list[:]:
            # 1、如果该动作有目标算子，如果目标算子已经死亡，则该动作不应该继续执行
            if "ops_id" in action:
                all_ops_status = {**utils.get_ops_status(RED), **utils.get_ops_status(BLUE)}
                if all_ops_status[action["ops_id"]]["live_status"] == False:
                    # 先记录日志
                    exec_action = {
                        "sponsor": "red" if action["color"] == 0 else "blue",
                        "type": action["type"],
                        "ops_id": action["ops_id"],
                        "result": "取消执行"
                    }
                    logs["exec_actions"].append(exec_action)

                    # 再将动作执行结果通过庙算提供的发送消息动作展示到将庙算复盘界面中
                    msg_body = ""
                    msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
                    if "ops_id" in action:
                        msg_body += "对目标算子 " + str(action["ops_id"]) + " "

                    msg_body += "发起动作 " + utils.get_info_action_name(action["type"]) + "，"
                    msg_body += "但由于目标算子当前已死亡，此动作取消执行"
                    sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
                    if sendMsg_action is not None:
                        sendMsg_actions.append(sendMsg_action)
                    # 最后剔除动作
                    current_actions.remove(action)
                    break

            # 2、检查前置条件为伪装接入的动作的前置条件在当前step下是否还成立
            if action["type"] in (301, 302, 402):
                all_ops_status = {**utils.get_ops_status(RED), **utils.get_ops_status(BLUE)}
                if all_ops_status[action["ops_id"]]["has_disguised_access"] == False:
                    # 先记录日志
                    exec_action = {
                        "sponsor": "red" if action["color"] == 0 else "blue",
                        "type": action["type"],
                        "ops_id": action["ops_id"],
                        "result": "取消执行"
                    }
                    logs["exec_actions"].append(exec_action)

                    # 再将动作执行结果通过庙算提供的发送消息动作展示到将庙算复盘界面中
                    msg_body = ""
                    msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
                    if "ops_id" in action:
                        msg_body += "对目标算子 " + str(action["ops_id"]) + " "

                    msg_body += "发起动作 " + utils.get_info_action_name(action["type"]) + "，"
                    msg_body += "但由于当前动作前置条件——目标算子 伪装接入状态 被解除，此动作取消执行"
                    sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
                    if sendMsg_action is not None:
                        sendMsg_actions.append(sendMsg_action)
                    # 最后剔除动作
                    current_actions.remove(action)
                    break
            # 3、检查前置条件为伪装接入和密钥破解的动作的前置条件在当前step下是否还成立
            if action["type"] == 403:
                all_ops_status = {**utils.get_ops_status(RED), **utils.get_ops_status(BLUE)}
                if all_ops_status[action["ops_id"]]["has_disguised_access"] == False or \
                        all_ops_status[action["ops_id"]]["has_cracked_key"] == False:
                    # 先记录日志
                    exec_action = {
                        "sponsor": "red" if action["color"] == 0 else "blue",
                        "type": action["type"],
                        "ops_id": action["ops_id"],
                        "result": "取消执行"
                    }
                    logs["exec_actions"].append(exec_action)

                    # 再将动作执行结果通过庙算提供的发送消息动作展示到将庙算复盘界面中
                    msg_body = ""
                    msg_body += ("【红方】" if action["color"] == 0 else "【蓝方】")
                    if "ops_id" in action:
                        msg_body += "对目标算子 " + str(action["ops_id"]) + " "

                    msg_body += "发起动作 " + utils.get_info_action_name(action["type"]) + "，"
                    msg_body += "但由于当前动作前置条件——目标算子 伪装接入+密钥破解状态 被解除，此动作取消执行"
                    sendMsg_action = utils.send_Msg_2_All(action["color"], msg_body)
                    if sendMsg_action is not None:
                        sendMsg_actions.append(sendMsg_action)

                    # 最后剔除动作
                    current_actions.remove(action)
                    break
